using System.Security.Principal;
using System.Web.Security;
using System;
using System.Configuration;
using System.Security;
using System.Web;

namespace IWCommunity.BasicAuth
{
    #region Imports

    using System;
    using System.Security.Principal;
    using System.Text;
    using System.Web;

    #endregion

    /// <summary>
    /// Enables ASP.NET applications to use Basic authentication as specified in
    /// RFC 2617 (HTTP Authentication: Basic and Digest Access Authentication).
    /// </summary>
    public class BasicAuthModule : IHttpModule
    {

        public BasicAuthModule() { }
        public void Dispose() { }
       

        public void Init(HttpApplication application)
        {
            application.AuthenticateRequest +=
               new EventHandler(this.OnAuthenticateRequest);
            application.EndRequest +=
               new EventHandler(this.OnEndRequest);
        }


        public void OnAuthenticateRequest(object source,EventArgs eventArgs)
        {
            HttpApplication app = (HttpApplication)source;

            // then follow authentication logic
            // get Http header "Authorization"
            // check if not empty
            string urlRequested = app.Request.Path.ToLower();
            string userAgent = app.Request.UserAgent;

            //only continue if it is an area which is supposed to be promted with a basic auth dialog box
            if (!isBasicAuthArea(urlRequested,userAgent))
                return;
            
            string authorization = app.Request.Headers["Authorization"];
            if ((authorization == null) || (authorization.Length == 0))
            {
                AccessDenied(app.Context);
                return;
            }

            // check if Http header has the syntax of basic
            authorization = authorization.Trim();
            if (authorization.IndexOf("Basic", 0) != 0)
            {
                AccessDenied(app.Context);
                return;
            }

            // cut the word "basic" and decode from base64 get "username:password"
            byte[] tempConverted = Convert.FromBase64String(authorization.Substring(6));
            string userInfo = new ASCIIEncoding().GetString(tempConverted);

            // get "username"
            // get "password"
            string[] usernamePassword = userInfo.Split(new char[] { ':' });
            string username = usernamePassword[0];
            string password = usernamePassword[1];

            // compare username, password against 
            // the values, stored in the database
            // if everything is fine, 
            // get user group list from the database
            // and create an instance of GenericPrincipal
            string[] groups;
            if (AuthenticateUser(username, password, out groups))
            {
                app.Context.User = new GenericPrincipal(new GenericIdentity(username,"Basic"),groups);
            }

             // else, AccessDenied
            else
            {
                AccessDenied(app.Context);
                return;
            
            }
        }

        private bool isBasicAuthArea(string urlRequested,string userAgent)
        {
            if (urlRequested.Contains("metaweblog.aspx"))
                return true;
            if (urlRequested.Contains("_vti_bin/lists.asmx") && userAgent.Contains("MS Web Services Client Protocol"))
                return true;
            if (urlRequested.Contains("_vti_bin/lists.asmx") && userAgent.Contains("Office Outlook"))
                return true;

            return false;

        }
     
        public void OnEndRequest(object source, EventArgs eventArgs)
        {
            HttpApplication app = (HttpApplication)source;
            if (app.Response.StatusCode == 302)
            {
                if (isBasicAuthArea(app.Request.Path.ToLower(),app.Request.UserAgent))
                {
                    string realm = String.Format("Basic Realm=\"Basic Auth\"");
                    app.Context.Response.AppendHeader("WWW-Authenticate", realm);
                    app.Context.Response.RedirectLocation = null;
                    app.Context.Response.StatusCode = 401;
                    app.Context.Response.StatusDescription = "Access Denied";
                }

            }
            else if (app.Response.StatusCode == 401)
            {
                // Show modal window, 
                // realm string is saved in web.config
                string realm = String.Format("Basic Realm=\"Basic Auth\"");                
                app.Context.Response.AppendHeader("WWW-Authenticate", realm);
            }
        }


        private void AccessDenied(HttpContext context)        {

            HttpResponse response = context.Response;
            response.StatusCode = 401;
            response.StatusDescription = "Access Denied";
            context.ApplicationInstance.CompleteRequest();
        }



        protected virtual bool AuthenticateUser(string userName, string Password, out string[] roles)
        {
            roles = null;

            try
            {
                //validate password
                if (Membership.ValidateUser(userName, Password))
                {
                    //GenericIdentity identity = new GenericIdentity(userName, "Basic");

                    try
                    {
                        //get the list of roles
                        roles = Roles.GetRolesForUser(userName);
                    }
                    catch
                    {
                        // Ignore if role provider is not setup
                    }

                    //return user
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                //some error logging
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message);
                //return nothing
                return false;
            }

           
        }
    }
}
